#define _CRT_SECURE_NO_WARNINGS 1



		《 Linux_10.预处理，编译，汇编，连接。 库 》


		《 yum 安装 g++ 》
        
安装: [root@192 c]# yum install gcc-c++ 	(没有空格 gcc-c++)

C++ 后缀一般分为 2种： .cpp / .cc
linux 默认不支持 C++ 编译，需要使用 g++ 进行编译
 g++ test.cc				//如果无法编译：
 g++ test.cc -std=c++11	//使用C++ 新标准11

[b@192 111]$ cat test.c
#include <stdio.h>
int main()
{
	for (int i = 0; i < 10; i++)
 	{
		printf("hello linux: %d\n", i);
	}
	return 0;
}
[b@192 111]$
[b@192 111]$ g++ test.c
- rwxrwxr - x. 1 b b 8400 Jan 26 01:54 a.out //生成默认文件
[b@192 111]$ . / a.out
hello linux : 0
hello linux : 1
hello linux : 2
hello linux : 3
hello linux : 4
hello linux : 5
hello linux : 6
hello linux : 7
hello linux : 8
hello linux : 9

————————————————————————————————————————————————————————————————


如果要升级
搜索：gcc/g++ scl 工具集升级，yum

		《 常见的C++ 后缀 》
            test.cc  
 mv test.cc test.cpp // 把.cc 后缀改为 .cpp //两个都是C++文件的后缀
 mv test.cc test.cxx

 查看g++版本
[b@192 111]$ g++ -v
[b@192 111]$ g++ --version
版本：g++ (GCC)4.8.5 20150623 (Red Hat 4.8.5 - 44)


		《 程序翻译的过程：》
【翻译过程】  【       gcc命令      】      【说明】
 预处理        gcc -E test.c -o test.i       宏替换，去注释，头文件展开，条件编译(-E 预处理完就停下，头文件展开:头文件中的内容拷贝到原文件中了)
               gcc -D V1=1 proj.c	        //命令行式的 宏定义：  只V1 生效：条件编译 实现动态裁剪
 编译          gcc -S test.i -o test.s       将C变成汇编语言:test.s

 汇编          gcc -c test.s -o test.o       汇编语言编译成为，二进制目标文件

 链接          gcc test.o -o my.exe          形成可执行程序(链接库)//链接完就可以正常执行文件了。(把我们的 程序 和 库 结合的过程)


test.o 二进制目标文件 是一个临时文件，即使加上权限 x 也是不能被执行的：
chmod +x test.o	//汇编完，即使加了可执行权限。也无法运行，因为它本身不是一个可执行文件。
//最后编译了才可以形成： 可执行文件 my.exe
[c@192 c]$ g++ test.o -o my.exe

注意：键盘左上角：ESc  与程序编译的顺序是一一匹配的。
 后缀 .i .s .o	//   与镜像文件后缀 .iso 一致。

//查看 test.o 文件的信息
[c@192 c]$ file test.o
test.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped


	《 Linux 中的库 》
 ls /usr/include/   //库的路径
 vim /usr/include/stdio.h   //打开 stdio.h 这个头文件，里面有900多行内容。


		《 预处理：》
 -E 的意思：执行完预处理就停下
[b@192 111]$ vim test.c
[b@192 111]$ gcc -E test.c -o test.i  // test.i 预处理后的内容放入 test.i 文件中 
- rw - rw - r--. 1 b b 16976 Jan 27 07 : 46 test.i   // 生成 .i文件
[b@192 111]$ vim test.i  // 打开文件后的内容就是预处理后的样子

Esc  进入命令模式
 vs test.c	// 分屏打开文件 test.c 
 Ctrl + ww   切换分屏


——————————————————————————————————————————————————————


——————————————————————————————————————————————————————


		《 条件编译 实现动态裁剪 》

//类似 普通版V1， 旗舰版V2， 专业版V3 的区别， 通过 动态裁剪 展现不同功能给用户！


#include <stdio.h>
#define V2 1		    // 定义了V2 ：V2 生效
int main() 
{
#ifdef V1               // (如果定义了 V1)
	printf("功能1\n");
#elif V2                // (如果定义了 V2)
	printf("功能1 \n"); 
	printf("功能2\n");
	printf("功能3\n");
#else                   // (否则运行这里)
	printf("功能1\n");
	printf("功能2\n"); 
	printf("功能3\n");
	printf("功能4\n");
	printf("功能5\n");
	printf("功能6\n");
#endif
		return 0;
}


	<命令行式的宏>
gcc -D V1=1 proj.c	// V1 生效：条件编译 实现动态裁剪： 在命令行模式进行手动裁剪

//裁剪出 V1 部分
[c@192 c]$ g++ -DV1=1 proj.c
[c@192 c]$ ./a.out 
功能1

//裁剪出 V2 部分
[c@192 c]$ g++ -DV2=1 proj.c
[c@192 c]$ ./a.out 
功能1 
功能2
功能3


——————————————————————————————————————————————————————


——————————————————————————————————————————————————————

	《 编译器的自举：》
 用二进制写了一个     ==> 汇编的编译器，
 用汇编的编译器写一个 ==> 纯汇编的编译器，
 新的汇编的编译器写   ==> C语言编译器
 C语言编译器写        ==> 纯C编译器

——————————————————————————————————————————————————————


————————————————————————————————————————————————————————


//      < 查看 可执行程序依赖的动态库 >
 libc-2.17.so ==> 《 C标准库 》
 ldd my.exe  ： my.exe可执行程序依赖的动态库查看
...
【 libc.so.6 = > / lib64 / libc.so.6(Ox00007fe3f8608000) 】
...
 lib64 / libc.so.6->libc - 2.17.so          ==> C标准库

 //     < 库名 >
	以 libc.so.6 为例：c.   就是:(C标准库)
 [去掉前缀 lib ]  [ c. 中间就是所属库(这里是C标准库)]  [ so.6 去掉后缀]


		《 头文件 》
[a@192 a]$ ls /usr/include/
aio.h        fpu_control.h   monetary.h  rpcsvc         ucontext.h
aliases.h    fstab.h         mqueue.h    sched.h        ulimit.h
alloca.h     fts.h           mtd         scsi           unistd.h

————————————————————————————————————————————————————————

		《 Linux 库的概念 》

<链接> :我的 [程序] 与 [库] 怎么链接的

因此链接时，存在两种链接方式：动态链接和静态链接
 动态库： .so  
 静态库： .a

动态链接：共享动态库:一但动态库缺失，所以依赖这个库的动态链接程序都无法执行了。    动态链接 需要 去到库里
静态链接：在编译的时候，把库中的方法，拷贝到自己的可执行程序中。不再依赖外部的库。 静态链接 无需 去到库里

C动态库，默认提供的
gcc默认形成的可执行程序，默认采用动态链接
动态库&& 动态链接的优缺点:
1.不能丢失            (缺点)
2.节省资源            (优点)
静态库 && 静态链接的优缺点 :
1.—旦形成，和库无关  (优点)
2.浪费资源            (缺点)

测试：
动态链接：
 [a@192 a]$  gcc -o mytest test.c
 dynamically linked(uses shared libs)   大小： 16472

静态链接：
 [a@192 a]$  gcc -o mytest-static test.c -static 
 statically linked                      大小：862272 大小相差几十倍

默认静态库是未安装的：
 C  静态库：glibc-static 
 C++静态库：libstdc++-static
yum安装 C 和 C++的静态库：
sudo yum install -y glibc-static libstdc++-static  


//查看文件的具体类型：file 
[c@192 c]$ file my.exe 
      :     64位的可执行程序，             ,                 ,           动态链接 (使用共享库)                    
my.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ad6c0886cbf9d6b2c1e7aff3d9d453ce9588e3a3, not stripped
[c@192 c]$ 

//以静态库的方式生成可执行程序:mytest-static
[c@192 c]$ g++ -o mytest-static test.c -static
[c@192 c]$ ll
total 1396
-rwxrwxr-x. 1 c    c      8360 May 20 05:55 mytest              //动态库
-rwxrwxr-x. 1 c    c    861216 May 20 06:09 mytest-static       //静态库(更大)
[c@192 c]$ 
//运行静态库的可执行程序:
[c@192 c]$ ./mytest-static 
hello Linux: 0
hello Linux: 1
hello Linux: 2
...
hello Linux: 9
[c@192 c]$ 


————————————————————————————————————————————————————————

		《 gcc选项 》
-E  只激活预处理, 这个不生成文件, 你需要把它重定向到一个输出文件里面. 
-S   编译到汇编语言不进行汇编和链接
-c  编译到目标代码
-o  文件输出到文件
-static  此选项对生成的文件采用静态链接 
-g  生成调试信息。GNU调试器可利用该信息。
-shared  此选项将尽量使用动态库，所以生成文件比较小，但是需要系统由动态库. - o0
-O1
-O2
-O3 编译器的优化选项的4个级别， - O0表示没有优化, -O1为缺省值， -O3优化级别最高 
-w  不生成任何警告信息。
-Wall 生成所有警告信息。


————————————————————————————————————————————————————————


————————————————————————————————————————————————————————



			《 make 和 makefile 》 


make     是一个命令
makefile 是一个文件

————————————————————————————————————————————————————————

mytest:test.c                     依赖关系 ：依赖文件列表，按照空格为分隔符 test.c test1.c test2.c (支持多个文件)
		gcc -o mytest test.c      依赖方法： ==>  (如何处理 这些文件)
.PHONY:clean                      总是要被执行的命令为：clean
clean:                            总是要被执行的 内容如下： 
		rm -f mytest              (换行后/ 输入 总是要被执行的 内容...)

————————————————————————————————————————————————————————

make会根据makefile的内容，完成编译 / 清理工作

mytest 默认形成第一个目标文件 

.PHONY: XXX
XXX对应的方法，总是要被执行的 ，比如清理项目的时候，我们希望总是被清理的。


a.为什么makefile对最新的可执行程序，默认不想重新形成呢 ?
b.怎么做到的 ?
makefile怎么知道我的程序需要被编译了呢 ?
只需要对比，可执行文件的最近修改时间和源文件最近的修改时间，谁更新 ?


//      《 stat 》
查看最新修改时间：
 stat test.c  //查看test.c 的最新编译时间
 Modify: 2024-03-12 23:14:12.643005259 -0700

最新编译时间： 一般情况源文件 创建时间 都比 编译时间 更早
 stat test.c  //查看test.c 的最新编译时间
 Modify: 2024-03-12 23:21:35.505484067 -0700

更改源文件修改时间：
touch test.c  //touch 除了新建文件，还可以跟新源文件的修改时间
 Modify: 2024-03-13 00:17:45.710206586 -0700


————————————————————————————————————————————————————————


————————————————————————————————————————————————————————

		《 make ：替换 》 

创建文件：makefile 中输入：
code.exe:code.c
	gcc -o $@ $^		 // $@ 会被替换成 code.exe // $^会被替换成code.c  ...(整个依赖文件列表)
.PHONY:clean
clean:
	rm -f code.exe

[a@192 a]$ cat makefile
code.exe:code.c
gcc -o $@ $^
.PHONY:clean  
rm - f code.exe
[a@192 a]$ make
gcc -o code.exe code.c

————————————————————————————————————————————————————————

打开文件 code.c
[a@192 a]$ vim code.c

写入程序
#include <stdio.h>
int main()
{
	printf("hehe\n");
	return 0;
}

 Esc :wq  保存并退出
[a@192 a]$ make				//make 编译一下
gcc -o code.exe code.c		//生成可执行程序 code.exe

[a@192 a]$ ./code.exe			//运行代码：
hehe							//输出: hehe

[a@192 a]$ make clean
rm - f code.exe

————————————————————————————————————————————————————————


		< # 是make 的注释 >

#code.exe:code.c
#			gcc - o $@ $^
#.PHONY:clean
#clean :
#			rm -f code.exe

要把上面代码写全了：
code.exe:code.o
    gcc code.o -o code.exe
code.o : code.s
    gcc -c code.s -o code.o
code.s : code.i
    gcc -S code.i -o code.s
code.i : code.c
    gcc -E code.c -o code.i		// ↑由下依次向上生成文件
.PHONY:clean						// 清理内容
clean :
rm - f code.i code.s code.o code.exe


[a@192 a]$ make					// 当我们重新 make 的时候，他会生成每一个步骤对应后缀的执行文件
gcc - E code.c -o code.i
gcc - S code.i -o code.s
gcc - c code.s -o code.o
gcc code.o -o code.exe 

[a@192 a]$ . / code.exe			// 最后运行 ： 输出 hehe
hehe

[a@192 a]$ make clean				// 清理以下文件，（清理makefile 中 clean 的文件）
rm - f code.i code.s code.o code.exe	//清理的文件

结论：
makefile / make会自动根据文件中的依赖关系，
进行自动推导，帮助我们执行所有相关的依赖方法，推导过程可以乱，但是最终文件不能乱序比如： code.exe 不能放到后面去
如果前面文件没有会向后推导：code.c --> code.i --> code.s --> code.o --> code.exe
类似于：入栈，然后出栈 的关系，递归。


————————————————————————————————————————————————————————

		《 多行注释参考 Linux_09 》
< 注释：>
1.  Ctrl + v 进入 -- VISUAL BLOCK -- 模式，
2.  n + Shift + g 从光标位置选中到 n 位置进行区域选择。 也可以 ↑ ↓ ← → 选择
3.  Shift + I  进入输入模式 ：输入 '#' 注释符
4.  Esc Esc 从光标位置到 n 行的位置(选择区域)全部注释。

< 删除注释 >
1.  Ctrl + v 进入 -- VISUAL BLOCK -- 模式，
2.  n + Shift + g + →（或者 + l） 从光标位置选中到 n 位置进行区域选择。 + → 加选1格注释 // 。 也可以 ↑ ↓ ← → 选择 
3.  d  删除

————————————————————————————————————————————————————————


		《 类似于#define 替换 》

bin=code.exe
src=code.c
 
#code.exe:code.c	// 前后对比
$(bin):$(src)		// 前后对比
      gcc -o $@ $^		

.PHONY:clean
clean :
		  rm -f $(bin)		// 前后对比
#       rm -f code.exe	//

[a@192 a]$ make			//执行程序 
gcc -o code.exe code.c	//效果一样

[a@192 a]$ make clean
rm -f code.exe			//清理掉文件code.exe

————————————————————————————————————————————————————————
行号 命令(a,b,cc 等名称可以随意替换)
  ↓  ↓
  1 a=code.exe          定义变量，类似 C语言的宏定义
  2 b=code.c
  3 $(a):$(b)
  4   gcc -o $@ $^      把 $@ == b 由 $^ == a 编译形成 b <== a    (code.exe <== code.c) 也可以直接  gcc -o $a $b 
  5 .PHONY:cc           输入 make cc 时，执行下面命令：  rm -f $(a)  删除code.exe
  6 cc:                                             
  7   rm -f $(a) 
  8 
  9 上下对比：
 10 #code.exe:code.c
 11 # gcc -o $@ $^      // $@提取 依赖关系里面的内容 ：$^提取 依赖文件列表里面的内容
 12 #.PHONY:clean
 13 #clean:
 14 # rm -f code.exe 


————————————————————————————————————————————————————————


		《 @ 》
 //执行该语句的时候不打印出来

bin = code.exe
src = code.c

$(bin) :$(src)
	@gcc -o $@ $ ^
	@echo "compiler $(src) to $(bin)..."

.PHONY:clean
clean :
	@rm - f $(bin)
	@echo "clean project..."


[a@192 a]$ vim makefile
[a@192 a]$ make
compiler code.c to code.exe...	//完成替换 并且不打印执行命令 @echo" "

[a@192 a]$ make clean
clean project...


————————————————————————————————————————————————————————

        《 快速更改 makefile 文件名 》
进入底行模式：%s/原文件名/新文件名/            (把所有 /原/改位新/ )

————————————————————————————————————————————————————————
